package initConfig

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/go-redis/redis/v8"
	"go.uber.org/zap"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var SqlDb *sql.DB             // 底层的 *sql.Db 实例
var GormDb *gorm.DB           // 全局数据库连接实例
var redisClient *redis.Client // Redis连接实例

// InitSqlDB 初始化 MySQL 数据库连接
func InitSqlDB(dbConfig SqlDatabaseConfig) {
	var err error

	// 动态生成 DSN (Data Source Name)
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		dbConfig.User,     // 用户名
		dbConfig.Password, // 密码
		dbConfig.Host,     // 主机地址
		dbConfig.Port,     // 端口
		dbConfig.Name,     // 数据库名称
	)

	// 打开数据库连接
	GormDb, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		zap.L().Fatal("无法连接数据库", zap.Error(err))
	}

	// 获取底层的 *sql.Db 实例
	SqlDb, err = GormDb.DB()
	if err != nil {
		zap.L().Fatal("无法获取底层的 *sql.Db 实例", zap.Error(err))
	}

	// Ping 数据库以确保连接成功
	err = SqlDb.Ping()
	if err != nil {
		zap.L().Fatal("无法 ping 通SQL数据库", zap.Error(err))
	}

	// 使用原生 SQL 创建表
	createTables()

	zap.L().Info("SQL数据库初始化成功！！！")
}

// createTables 手动执行 SQL 脚本创建表
func createTables() {
	// 定义 SQL 脚本
	// 用户表
	createUsersTable := `
	CREATE TABLE IF NOT EXISTS user (
		id VARCHAR(36) PRIMARY KEY NOT NULL, # 用户id，uuid
		email VARCHAR(20),                   # 邮箱
    	phone VARCHAR(12),                   # 电话号码
		password VARCHAR(200),               # 密码
		username VARCHAR(10),                # 昵称
		gender VARCHAR(4),                   # 性别
		birthday VARCHAR(20),                # 生日
		avatar VARCHAR(100),                 # 头像
    	is_manager TINYINT                   # 是否是管理员 *0为普通用户，1为管理员*
	);`

	// 历史头像表
	createHistoryAvatarTable := `
	CREATE TABLE IF NOT EXISTS history_avatar (
	    id VARCHAR(36) PRIMARY KEY NOT NULL,   # 头像的id，uuid
    	avatar VARCHAR(100),                   # 头像的url
    	userID VARCHAR(36),                    # 用户id，作为外键
    	FOREIGN KEY (userID) REFERENCES user(id) ON DELETE CASCADE
	);`

	// 动物信息表
	createAnimalsInformationTable := `
	CREATE TABLE IF NOT EXISTS animals_information (
		id VARCHAR(36) PRIMARY KEY NOT NULL,   # 动物的id，uuid
		name VARCHAR(10),                      # 动物名字
		age TINYINT UNSIGNED,                  # 动物年龄 *正整数*
		type VARCHAR(10),                      # 动物类型 *小猫，小狗，小猪。。。*
		uploaded_at VARCHAR(20),               # 上传至平台时间（弃养时间）*string类型 2025-05-31*
		uploader_id VARCHAR(36),               # 上传人ID
		information VARCHAR(100),              # 相关信息描述 *100字内*
		picture1 VARCHAR(100),                 # 动物照片1  *url*
		picture2 VARCHAR(100),                 # 动物照片2  *url*
		picture3 VARCHAR(100),                 # 动物照片3  *url*
		is_adopted TINYINT                     # 是否已被领养 *0为未被领养，1为已被领养*
	);`

	// 领养表
	createAdoptionTable := `
	CREATE TABLE IF NOT EXISTS adoption (
		id VARCHAR(36) PRIMARY KEY NOT NULL,        # 每条领养记录的id，uuid
		animal_id VARCHAR(36) NOT NULL,             # 被领养动物的id，外键
		adopter_id VARCHAR(36) NOT NULL,            # 领养者的id，外键
		adoption_at VARCHAR(20),                    # 领养时间 *string类型 格式：2025-06-01*
		FOREIGN KEY (animal_id) REFERENCES animals_information(id) ON DELETE CASCADE,  # 级联删除
		FOREIGN KEY (adopter_id) REFERENCES user(id) ON DELETE CASCADE                 # 级联删除
	);`

	// 执行 SQL 脚本  调用顺序不能变
	execSQL(createUsersTable)
	execSQL(createHistoryAvatarTable)
	execSQL(createAnimalsInformationTable)
	execSQL(createAdoptionTable)
}

// execSQL 执行 SQL 语句并处理错误
func execSQL(query string) {
	_, err := SqlDb.Exec(query)
	if err != nil {
		zap.L().Fatal("执行 SQL 失败", zap.String("query", query), zap.Error(err))
	}
}

// InitRedisDB 初始化 Redis 数据库连接
func InitRedisDB(dbConfig RedisDatabaseConfig) {
	// 创建 Redis 客户端
	redisClient = redis.NewClient(&redis.Options{
		Addr:     fmt.Sprintf("%s:%d", dbConfig.Host, dbConfig.Port),
		Password: dbConfig.Password,
		DB:       dbConfig.DB,
		PoolSize: dbConfig.PoolSize,
	})

	// 上下文用于 Ping 检查
	ctx := context.Background()

	// 测试连接
	if err := redisClient.Ping(ctx).Err(); err != nil {
		zap.L().Fatal("无法连接到Redis", zap.Error(err))
	}

	zap.L().Info("Redis数据库初始化成功！！！")
}

// GetRedisClient 返回已初始化的 Redis 客户端
func GetRedisClient() *redis.Client {
	return redisClient
}
